#include <iostream>
#include <boost/asio.hpp>
#include <boost/xpressive/xpressive_dynamic.hpp>
#include <boost/array.hpp>
#include <thread>
#include <condition_variable>
#include <mutex>
#include <atomic>
#include <queue>

std::wstring ASCIIStringToWString(const std::string& str)
{
#ifdef _MSC_VER
	int wcsLen = ::MultiByteToWideChar(CP_ACP, NULL, str.c_str(), static_cast<int>(str.size()), NULL, 0);
	wchar_t* wszString = new wchar_t[wcsLen + 1];
	::MultiByteToWideChar(CP_ACP, NULL, str.c_str(), static_cast<int>(str.size()), wszString, wcsLen);

	wszString[wcsLen] = '\0';
	std::wstring unicodeString(wszString);
	delete[] wszString;
	wszString = NULL;
	return unicodeString;
#else
	std::wstring val = L"";
	size_t size_of_wc;
	size_t destlen = mbstowcs(0, str.c_str(), 0);
	if (destlen != (size_t)(-1))
	{
		size_of_wc = destlen + 1;
		wchar_t* pw = new wchar_t[size_of_wc];
		if (pw)
		{
			memset(pw, 0, size_of_wc * sizeof(wchar_t));
			mbstowcs(pw, str.c_str(), size_of_wc);
			val = pw;
			delete[] pw;
			pw = nullptr;
		}

	}
	return val;
#endif
}

std::string WStringToUTF8String(const wchar_t* lpwcszWString)
{
#ifdef _MSC_VER
	char* pElementText(nullptr);
	int iTextLen = ::WideCharToMultiByte(CP_UTF8, 0, (LPWSTR)lpwcszWString, -1, NULL, 0, NULL, NULL);
	pElementText = new char[iTextLen + 1];
	memset((void*)pElementText, 0, (iTextLen + 1) * sizeof(char));
	::WideCharToMultiByte(CP_UTF8, 0, (LPWSTR)lpwcszWString, -1, pElementText, iTextLen, NULL, NULL);
	std::string strReturn(pElementText);
	delete[] pElementText;
	return strReturn;
#else
	std::string val = "";
	if (lpwcszWString != nullptr)
	{
		size_t size = wcslen(lpwcszWString) * sizeof(wchar_t);
		char* pc = new char[size];
		if (pc)
		{
			memset(pc, 0, size * sizeof(char));
			size_t destlen = wcstombs(pc, lpwcszWString, size);
			if (destlen != (size_t)(0))
			{
				val = pc;
			}
			delete[] pc;
			pc = nullptr;
		}
	}
	return std::move(val);
#endif

}

std::string ASCIIStringToUTF8String(const std::string& str)
{
#ifdef _MSC_VER
	std::wstring wstr = ASCIIStringToWString(str);
	std::string ustr = WStringToUTF8String((wchar_t*)(wstr.c_str()));
	return std::move(ustr);
#else
	return str;
#endif
}

#define TO_8(src)  ASCIIStringToUTF8String(src)


//BOOST用正则表达式验证ip地址合法
bool CheckIP(const char *ip)
{
	using namespace boost::xpressive;
	/* 定义正则表达式 */
	cregex reg_ip = cregex::compile("(25[0-4]|2[0-4][0-9]|1[0-9][0-9]|[1-9][0-9]|[1-9])[.](25[0-5]|2[0-4][0-9]|1[0-9][0-9]|[1-9][0-9]|[0-9])[.](25[0-5]|2[0-4][0-9]|1[0-9][0-9]|[1-9][0-9]|[0-9])[.](25[0-4]|2[0-4][0-9]|1[0-9][0-9]|[1-9][0-9]|[1-9])");
	return  regex_match(ip, reg_ip);
}

class BroadSender
{
public:
	BroadSender()
	{

	}
	~BroadSender()
	{

	}
	void SetSocket(std::shared_ptr<boost::asio::ip::udp::socket> _socket)
	{
		m_SendMsgSockPtr = _socket;
	}

	bool SendBroadCastMsg(const std::string& _interfaces, const unsigned short& _port, const void* _msg, size_t _size)
	{
		const std::string  allInterface = boost::asio::ip::address_v4::broadcast().to_string();
		bool bOk = CheckIP(_interfaces.c_str());
		if (!bOk  && allInterface.compare(_interfaces) != 0)
		{
			return false;
		}
		std::string broadCastInterface = _interfaces;
		if (_interfaces != allInterface)
		{
			size_t index = _interfaces.find_last_of('.');
			auto t_Addr = _interfaces.substr(0, index);
			broadCastInterface = t_Addr + ".255";
		}

		try {
			boost::asio::ip::udp::endpoint broadcast_endpoint(boost::asio::ip::address::from_string(broadCastInterface), _port);
			if (!m_SendMsgSockPtr)
			{
				return false;
			}
			size_t t_SendMsgSize = m_SendMsgSockPtr->send_to(boost::asio::buffer(_msg, _size), broadcast_endpoint);
			if (t_SendMsgSize != _size)
			{
				return false;
			}
			return true;
		}
		catch (const boost::system::error_code& ex)
		{
			std::cout << "异常1:" << ex << std::endl;
		}
		catch (const std::exception& ex) {
			std::cout << "异常2:" << TO_8(ex.what()) << std::endl;
		}
		catch (...) {
			std::cout << "未知异常" << std::endl;
		}
		return false;
	}

	bool SendToMsg(const std::string& destIp, const unsigned short& destPort, const void* msg, size_t size)
	{
		if (!msg || size <= 0)
		{
			return false;
		}

		bool bOk = CheckIP(destIp.c_str());
		if (!bOk)
		{
			return false;
		}

		try
		{
			boost::asio::ip::udp::endpoint t_DestEndpoint(boost::asio::ip::address::from_string(destIp), destPort);
			size_t t_SendMsgSize = m_SendMsgSockPtr->send_to(boost::asio::buffer(msg, size), t_DestEndpoint);
			if (t_SendMsgSize != size)
			{
				return false;
			}

		}
		catch (const boost::system::error_code& ex)
		{
			std::cout << "异常:" << ex << std::endl;
		}
		catch (const boost::system::system_error& ex)
		{
			std::cout << "异常:" << ex.what() << std::endl;
		}
		catch (const std::exception&ex)
		{
			std::cout << "异常:" << ex.what() << std::endl;
		}
		return  true;
	}

private:
	std::shared_ptr<boost::asio::ip::udp::socket>	m_SendMsgSockPtr = nullptr;
};

enum MsgType
{
	BROADCAST_ALAEVESRV,
	REPLY_ALAEVESRV
};

struct MsgBase
{
	MsgType	m_MsgType;
};
struct BroadCastAlaEveSrvMsg :public MsgBase
{
	char m_Msg[255] = { 0 };
	char m_ProjectID[128] = { 0 };
	char m_ProjectName[128] = { 0 };
};

struct Message
{
	long			m_RecvTimeS;
	char			m_FromIp[16];
	unsigned short  m_FromPort;
	char			m_Buf[1024];
	unsigned short	m_Length;
};

class  BroadReciever
{
public:
	void SetMsgCallBack(const std::function<void(std::shared_ptr<Message>)>& func)
	{
		m_CallBackFunc = func;
	}
	void StartRecvMsg()
	{
		do_receive();
		m_CallBackThread = std::make_shared<std::thread>(std::bind(&BroadReciever::StartPopMsg, this));
	}
	void SetSocket(std::shared_ptr<boost::asio::ip::udp::socket>_socket)
	{
		m_SocketPtr = _socket;
	}
	bool Stop()
	{
		m_Exit = true;
		if (m_CallBackThread)
		{
			if (m_CallBackThread->joinable())
			{
				m_CallBackThread->join();
			}

			m_CallBackThread.reset();
		}
		return true;
	}
private:
	void do_receive()
	{
		try
		{
			m_buf.fill(0);
			m_SocketPtr->async_receive_from(
				boost::asio::buffer(m_buf), m_sender_endpoint,
				[this](boost::system::error_code ec, std::size_t length)
			{
				if (!ec)
				{
					unsigned short  t_Port = m_sender_endpoint.port();
					const std::string t_Ip = m_sender_endpoint.address().to_v4().to_string();
					std::shared_ptr<Message> t_Msg = std::make_shared<Message>();
					memset(t_Msg->m_Buf, 0, sizeof(t_Msg->m_Buf));
					memset(t_Msg->m_FromIp, 0, sizeof(t_Msg->m_FromIp));
					t_Msg->m_FromPort = t_Port;
					t_Msg->m_Length = length;
					memcpy(t_Msg->m_FromIp, t_Ip.c_str(), t_Ip.length());
					memcpy(t_Msg->m_Buf, m_buf.data(), length);
					boost::posix_time::ptime epoch(boost::gregorian::date(1970, boost::gregorian::Jan, 1));
					boost::posix_time::time_duration time_from_epoch =
						boost::posix_time::second_clock::universal_time() - epoch;
					t_Msg->m_RecvTimeS = time_from_epoch.total_seconds();

					m_MsgQueue.push(t_Msg);
					m_MsgQueue_cv.notify_all();
				}
				do_receive();
			});
		}
		catch (...)
		{
			std::cout << "接收数据异常" << std::endl;
		}
	}
	void StartPopMsg()
	{
		while (!m_Exit)
		{
			std::unique_lock<std::mutex> lck(m_MessageQueue_mtx);
			m_MsgQueue_cv.wait(lck, [this]()->bool {
				return (0 == m_MsgQueue.size()) ? false : true;
			});
			std::shared_ptr<Message> t_Msg = m_MsgQueue.front();
			if (m_CallBackFunc)
			{
				m_CallBackFunc(t_Msg);
			}
			m_MsgQueue.pop();
		}
	}
private:
	boost::array<char, 1024> m_buf;
	std::shared_ptr<boost::asio::ip::udp::socket>	m_SocketPtr = nullptr;
	std::shared_ptr<std::thread>	m_CallBackThread = nullptr;
	std::function<void(std::shared_ptr<Message>)>	m_CallBackFunc = nullptr;
	std::queue<std::shared_ptr<Message>> m_MsgQueue;
	boost::asio::ip::udp::endpoint m_sender_endpoint;
	std::atomic_bool	m_Exit = false;
	std::condition_variable m_MsgQueue_cv;
	std::mutex m_MessageQueue_mtx;
};

int main()
{
	const int udpPort = 11089;
	const std::string udpIP = "192.168.29.1";
	system("chcp 65001");
	try {
		boost::asio::io_service io_Server;
		boost::asio::ip::udp::endpoint endpoint(boost::asio::ip::udp::v4(), udpPort);
		std::shared_ptr<boost::asio::ip::udp::socket> pSocket = std::make_shared<boost::asio::ip::udp::socket>(io_Server, endpoint);
		pSocket->set_option(boost::asio::socket_base::broadcast(true));
		//启动ioserver
		std::shared_ptr<BroadSender> pBroadSender = std::make_shared<BroadSender>();
		pBroadSender->SetSocket(pSocket);


		//开始发送组播
		std::thread t3([pBroadSender, udpPort, udpIP] {
			while (true) {
				std::this_thread::sleep_for(std::chrono::seconds(3));
				std::cout << "开始发送UDP组播..." << std::endl;

				/*{
					BroadCastAlaEveSrvMsg broadCastAlaEveSrvMsg;
					memset(&broadCastAlaEveSrvMsg, 0, sizeof(broadCastAlaEveSrvMsg));
					broadCastAlaEveSrvMsg.m_MsgType = MsgType::BROADCAST_ALAEVESRV;
					const std::string broadCastMsgContent = "udpTest";
					const std::string strProjectID = "123";
					const std::string strProjectName = "localhost";
					memcpy(&broadCastAlaEveSrvMsg.m_Msg, broadCastMsgContent.c_str(), broadCastMsgContent.length());
					memcpy(broadCastAlaEveSrvMsg.m_ProjectID, strProjectID.c_str(), strProjectID.length());
					memcpy(broadCastAlaEveSrvMsg.m_ProjectName, strProjectName.c_str(), strProjectName.length());
					pBroadSender->SendBroadCastMsg("192.168.29.106", udpPort, (void*)&broadCastAlaEveSrvMsg, sizeof(broadCastAlaEveSrvMsg));

				}*/

				char buf[] = "hello";
				pBroadSender->SendBroadCastMsg(udpIP, udpPort, buf, sizeof(buf));
			}
		});
		t3.detach();


		//接收消息
		std::shared_ptr<BroadReciever> pBroadReciever = std::make_shared<BroadReciever>();
		pBroadReciever->SetSocket(pSocket);
		pBroadReciever->SetMsgCallBack([](std::shared_ptr<Message> _pMessage) {
			std::cout << "接收到【" << _pMessage->m_FromIp << "】的消息:" << _pMessage->m_Buf << std::endl;
		});
		pBroadReciever->StartRecvMsg();

		io_Server.run();
	}
	catch (const std::exception& ex) {
		std::cout << "异常:" << TO_8(ex.what()) << std::endl;
	}
	system("pause");
	return 0;
}